#include "device/CLINT.h"

CLINT::CLINT() {
    mtimecmp = 0;
    mtime = 0;
    msip = 0;
}

void CLINT::tick() {
    mtime++;
}

bool CLINT::read(word_t start_addr, size_t length, uint8_t* buffer) {
    size_t offset = start_addr - CLINT_BASE;
    // msip
    if (offset >= 0 && offset < 4) {
        memcpy(buffer, (uint8_t*)&msip + offset, length);
        return true;
    }
    // mtimecmp
    if (offset >= CLINT_MTIMECMP_OFFSET && offset < CLINT_MTIMECMP_OFFSET + 8) {
        memcpy(buffer, (uint8_t*)&mtimecmp + (offset - CLINT_MTIMECMP_OFFSET), length);
        return true;
    }
    // mtime
    if (offset >= CLINT_MTIME_OFFSET && offset < CLINT_MTIME_OFFSET + 8) {
        memcpy(buffer, (uint8_t*)&mtime + (offset - CLINT_MTIME_OFFSET), length);
        return true;
    }
    return false;
}

bool CLINT::write(word_t start_addr, size_t length, const uint8_t *buffer) {
    size_t offset = start_addr - CLINT_BASE;
    // msip
    if (offset >= 0 && offset < 4) {
        memcpy((uint8_t*)&msip + offset, buffer, length);
        return true;
    }
    // mtimecmp
    if (offset >= CLINT_MTIMECMP_OFFSET && offset < CLINT_MTIMECMP_OFFSET + 8) {
        memcpy((uint8_t*)&mtimecmp + (offset - CLINT_MTIMECMP_OFFSET), buffer, length);
        return true;
    }
    // mtime
    if (offset >= CLINT_MTIME_OFFSET && offset < CLINT_MTIME_OFFSET + 8) {
        memcpy((uint8_t*)&mtime + (offset - CLINT_MTIME_OFFSET), buffer, length);
        return true;
    }
    return false;
}

void CLINT::update() {
    // do nothing
}

bool CLINT::msi() {
    return msip & 1;
}

bool CLINT::mti() {
    return mtime >= mtimecmp;
}
